/*
 * Created by IntelliJ IDEA.
 * User: rousseau
 * Date: Apr 21, 2002
 * Time: 3:16:48 PM
 * To change template for new class use
 * Code Style | Class Templates options (Tools | IDE Options).
 */
package org.jfract.util;

import org.jfract.abstractBeans.AbstractFractalBean;

/**
 * This class is a thread. The goal is to compute the graphics with a thread that give a time for the processor
 */
public class PictureProcessor
    extends Thread
{
  private CompletionListener listener;

  private Object threadLock = new Object();

  public Object getThreadLock()
  {
    return threadLock;
  }

  private boolean running = true;

  private boolean pause = false;

  // private int minIter = Integer.MAX_VALUE;
  // private int maxIter = 0;
  // private double maxCalcul = 0;

  private AbstractFractalBean fractalBean;

  private int width;

  private int height;

  public void setRunning(boolean running)
  {
    this.running = running;
  }

  /**
   * notify the thread
   */
  public void notifyThread()
  {
    this.pause = false;
    synchronized (threadLock)
    {
      threadLock.notify();
    }
  }

  /**
   * pause the thread wait on lock object
   */
  public void pause()
  {
    synchronized (threadLock)
    {
      try
      {
        threadLock.wait();
      }
      catch (InterruptedException e)
      {
        e.printStackTrace();
      }
    }
  }

  public void stopThread()
  {
    interrupt();
  }

  /**
   * the constructor
   * 
   * @param fractalBean
   *          the fractal bean
   * @param img
   *          the bufferedImage to paint on
   * @param listener
   *          the completion listener to tell the completion
   * @param repaint
   *          reapint the componant at the end
   */
  public PictureProcessor(AbstractFractalBean fractalBean, int width,
      int height, CompletionListener listener, int[] refreshLevels)
  {
    this.fractalBean = fractalBean;
    this.listener = listener;
    this.setPriority(Thread.NORM_PRIORITY);
    this.width = width;
    this.height = height;
    this.fractalBean.setCompletionListener(listener);
    this.fractalBean.setWidth(width);
    this.fractalBean.setHeight(height);
    if (refreshLevels != null)
    {
      fractalBean.setRefreshLevels(refreshLevels);
    }
    fractalBean.setProcessor(this);
  }

  private int[] resultColor = null;

  private boolean finished;

  public boolean isFinished()
  {
    return finished;
  }

  public int[] getResultColor()
  {

    return resultColor;
  }

  // limited size on JNI size
  private static int MAX_ARRAY_SIZE = 90000;

  // private static int MAX_ARRAY_SIZE = 20;

  /**
   * If this thread was constructed using a separate <code>Runnable</code> run object, then that <code>Runnable</code> object's <code>run</code>
   * method is called; otherwise, this method does nothing and returns.
   * <p>
   * Subclasses of <code>Thread</code> should override this method.
   * 
   * @see java.lang.Thread#start()
   * @see java.lang.Thread#stop()
   * @see java.lang.Thread#Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String)
   * @see java.lang.Runnable#run()
   */
  public void run()
  {

    int i, j;

    Dimension dim = new Dimension(width, height);
    int totalPixels = width * height;
    fractalBean.setTotalPixels(totalPixels);
    fractalBean.computeDelta(width, height);
    fractalBean.computeDelta(width, height);

    int cut = MAX_ARRAY_SIZE / width;
    int cutHeightIndex[] = new int[(totalPixels / MAX_ARRAY_SIZE) + 2];
    // cutHeightIndex[0] = -1;
    for (int k = 0; k < cutHeightIndex.length - 1; k++)
    {
      cutHeightIndex[k] = k * cut;
    }
    cutHeightIndex[cutHeightIndex.length - 1] = height;

    // TODO : generate an out of memory, change it
    float[] inputs = new float[totalPixels * 2];
    for (int k = 0; k < cutHeightIndex.length - 1; k++)
    {
      if (cutHeightIndex[k] < height)
      {
        float[] inputsCut = computeDelta(fractalBean.getXmin(), fractalBean.getDeltax(), fractalBean.getYmin(), fractalBean.getDeltay(), width, cutHeightIndex[k], cutHeightIndex[k + 1]);
        System.arraycopy(inputsCut, 0, inputs, (cutHeightIndex[k]) * 2 * width, (cutHeightIndex[k + 1] - cutHeightIndex[k]) * 2 * width);
      }
    }

    fractalBean.setValues(inputs);
    fractalBean.compute();
     finished = true;

    // if (listener != null)
    // listener.completionEventPerformed(totalPixels , totalPixels);

  }

  private native float[] computeDelta(float xMin, float deltaX, float yMin,
      float deltaY, int width, int startHeight, int endHeight);

}
